home *** CD-ROM | disk | FTP | other *** search
/ Network Support Library / RoseWare - Network Support Library.iso / apidev / mgabra.arc / MGABRA.DOC < prev    next >
Text File  |  1990-01-22  |  51KB  |  1,126 lines

  1. ABRACADABRA! For Turbo C Version 1.0
  2.  
  3. SECTION 1 INTRODUCTION
  4.  
  5. Welcome to Magic Software's Instant Resident Utility Maker 
  6. ABRACADABRA. Hopefully in no time at all we will be having you 
  7. create some amazing TSR programs with a simplicity BEFORE NOW 
  8. unheard of. We have also here at Magic coined a new term TCR 
  9. meaning Terminate but Continue Running which describes programs 
  10. which continue to run concurrently even while you are using the 
  11. pc to run a main program. These are just as easy to create with 
  12. ABRACADABRA.
  13.  
  14. For me, writing TSRs used to be like first learning to program. 
  15. Without a library to facilitate me or even the know how to build 
  16. one I was somewhat in the dark. There wasn't even a book on the 
  17. subject. I was later to learn why. The secrets of this activity 
  18. were being guarded heavily by the few who knew them. I thought 
  19. this was completely at odds with the hacker ethic but what could 
  20. I do? Well after a few years of hobnobbing with these nuts, 
  21. geniuses and boneheads, each of whom had a piece of the puzzle I 
  22. was able to gather them all together and modularize them into 
  23. ABRACADABRA. 
  24.  
  25. For most of you, the fact that you were even interested in this 
  26. library guarantees you will have a fascinating time of it as 
  27. these secrets are revealed.
  28.  
  29. If you are already familiar with these internals you can skip 
  30. right to the section describing the library primitives otherwise 
  31. read on.
  32.  
  33. SECRETS OF TSR PROGRAMMING
  34.  
  35. What makes a TSR a TSR? Certainly NOT just the fact that it 
  36. remains resident. Device drivers loaded in your CONFIG.SYS file 
  37. remain resident but they are not generally considered a TSR. A 
  38. TSR has several distinct qualities that define it.
  39.  
  40. 1. It remains in memory after initial loading from disk and can 
  41. be instantly invoked from memory.
  42.  
  43. 2. The invocation of a TSR must be able to occur WITHIN another 
  44. program. That's what gives them their utility value.
  45.  
  46. 3. The interrupted program can resume running after the TSR is 
  47. switched out.
  48.  
  49. 4. An additional quality we will add for a TCR (Terminate 
  50. Continue Running) is that it can continue to execute even after 
  51. it has been switched out.
  52.  
  53. When you are writing TSRs you are engaging in a limited degree of 
  54. multitasking. ABRACADABRA lets you take it to a full program swap 
  55. which means the ENTIRE program is switched, stack, data and all 
  56. INCLUDING screen and any disk access related memory items.
  57.  
  58. It is no small feat to program but all this work has been 
  59. modularized for you in ABRACADABRA. If you have purchased the 
  60. source code then you can follow along in the next section, 
  61. otherwise you can skip right to the description of the 
  62. ABRACADABRA library functions and how to use them.
  63.  
  64. What makes it so difficult? Why isn't it easy to write TSRs?
  65.  
  66. Actually the program which swaps programs while complex, is not 
  67. the hardest part of this whole scenario. While complex, it is 
  68. quite abstract and orderly because it is a transaction that 
  69. occurs entirely between the CPU and memory. The 8086 has always 
  70. been somewhat friendly in those terms.
  71.  
  72. The difficulties occur as you get further into the real world 
  73. away from the protected confines of the CPU and into the program 
  74. jungle called MS-DOS. DOS is essentially hostile toward attempts 
  75. at making it a multitasking program manager. So we have to do all 
  76. kinds of greasy kid stuff to make it comply with our wishes. 
  77. While the problem is complex, it is finite.
  78.  
  79. The core of the problem, switching programs, is handled by simply 
  80. saving the swapped out programs complete register set and 
  81. replacing it with the swapped in programs registers. You can 
  82. examine this in more detail in the source listing of the macros 
  83. SAVPRC (save process) and RESPRC (restore process). When a 
  84. process is put to sleep it's registers are stored in a special 
  85. area loosely called the TASK CONTROL BLOCK (TCB). Other relevant 
  86. data is also stored here and we'll cover that next.
  87.  
  88. Besides the registers, there is other baggage that each program 
  89. has attached that must also be switched. This consists of special 
  90. data areas DOS sets up for disk access, screen contents, video 
  91. modes, cursor position, and special interrupt addresses each 
  92. program uses for critical error handling and control-c break 
  93. checking.
  94.  
  95. The unfriendly attitude DOS has regarding multitasking is very 
  96. deeply rooted in it's structure. A quick summary of what DOS is, 
  97. is simply a set of functions that a program can call to do 
  98. things. In this manner it is no different than a function library 
  99. you get with compiler type languages. It is designed however to 
  100. be called from anywhere. You don't have to know the actual 
  101. address of each function, you just load up your parameters and 
  102. execute a software interrupt 21h. This gives control of the 
  103. machine to DOS with the parameters you pass. It executes your 
  104. desire and returns control to your program when it is done. 
  105. However, DOS routines, like some languages, cannot handle 
  106. overlapping calls. You must finish one before another is 
  107. undertaken. This is because when you make a call, DOS switches 
  108. from your program's stack to it's own internal set. Whatever is 
  109. pushed here must be fully popped. If you try to call DOS before 
  110. it has finished a previous cycle everything goes fine but when 
  111. the previous cycle tries to complete it finds it's stack data has 
  112. been trashed. This is unfortunate because in a multitasking 
  113. environment you have programs which are all requesting DOS 
  114. assistance at roughly the same time. 
  115.  
  116. The way around this is to check if DOS is currently executing an 
  117. internal routine and have our programs wait until it is done 
  118. before requesting a subsequent service. A good solution would be 
  119. to latch onto the interrupt 21h and set a semaphore (flag) 
  120. whenever someone went through there and turn it off when they 
  121. came back out. That way we'd know when DOS was occupied. 
  122. Surprisingly DOS itself does just this and that flag is available 
  123. to our programs. There is an undocumented DOS feature which most 
  124. programmers call "The Dos Busy Flag" or "The Dos Critical Flag". 
  125. This flag exists at an address we can get by calling DOS function 
  126. 34h. You won't find this in any Microsoft documentation. It is 
  127. simply listed as "Used Internally By Dos". So we get that address 
  128. and check that flag whenever we want to access DOS and if it's 
  129. busy we wait until it isn't.
  130.  
  131. Because DOS is so finicky about when it can be called we cannot 
  132. simply invoke our TSR whenever someone asks. We must be polite 
  133. and wait until any DOS services are complete before we take over. 
  134. To do this we have to know whether DOS is busy or not.  
  135.  
  136. Now there is another set of routines called the BIOS (BASIC INPUT 
  137. OUTPUT SYSTEM) which is like DOS's DOS. DOS calls the BIOS when 
  138. it needs a nice prepackaged routine to access peripheral devices. 
  139. The BIOS has routines which are solely designed to organize 
  140. access to various devices on the PC such as disk drives, monitors 
  141. and clocks. The BIOS is actually stored in a chip on your pc and 
  142. each machine manufacturer supplies a BIOS (or should) when you 
  143. buy the machine. DOS is aptly named because it is a DISK 
  144. OPERATING SYSTEM. It's routines are very strong in handling disk 
  145. access but very weak in other areas. These weaknesses have made 
  146. programming the BIOS as common as using DOS. Something that 
  147. wasn't intended but has come about. Sort of like TSRs.
  148.  
  149. The BIOS is particularly strong in video routines. It can also be 
  150. accessed directly by our programs if need be. Of course the 
  151. reason for all these layers of routines is to have each layer 
  152. present an identical appearance to our programs no matter what 
  153. machine we are running on from Hewlett Packard to real IBM. By 
  154. bypassing DOS it would seem we sacrifice a small degree of 
  155. compatibility from manufacturer to manufacturer but in fact an 
  156. IBM compatible must have a compatible BIOS these days since just 
  157. about every program written bypasses DOS in some way or another 
  158. especially for video display which DOS has hideously poor 
  159. provision for. There doesn't even exist DOS functions to change 
  160. colors on the monitor it's so deficient.
  161.  
  162. Thankfully the BIOS which is pretty compatible from machine to 
  163. machine is really only routines and no substitute stack areas. 
  164. When you call the BIOS it simply assumes your program's stack has 
  165. enough room to accommodate whatever it needs and doesn't bother 
  166. to swap at all. Thus it can be called and interrupted any number 
  167. of times. ABRACADABRA uses the BIOS a lot to "get around" DOS. 
  168. When a routine can be called again before it is finished it is 
  169. said to be re-entrant. You can RE ENTER it before it is finished. 
  170. Smartly written this BIOS. 
  171.  
  172. Now what do we want to accomplish with a TSR? Mainly we want that 
  173. facility instantly available. Ok, so that means we need to keep 
  174. the TSR in memory right? Ok so we need have a module which 
  175. handles the terminate aspect and the necessary memory allocation 
  176. associated with that. What else? Yes you ... That's right, we 
  177. want to be able to access it with a keystroke so we will have to 
  178. be tinkering with the way the PC accepts keystrokes. We need to 
  179. be able to program HOTKEYS. Oh yes, and because our programs can 
  180. optionally continue to run "in background" we will be looking at 
  181. something called TIMER TICK which we will use to control how long 
  182. each program gets control before is is forced to swap out in 
  183. order to share CPU time. And of course because we are possibly 
  184. using ABRACADABRA in conjunction with a compiled language we will 
  185. need to know a little bit about run time memory architecture of 
  186. compiled languages. Also, because we need to be able to control 
  187. the screen behavior when programs swap we will be delving into 
  188. that.
  189.  
  190. COMPILED LANGUAGE ARCHITECTURE
  191.  
  192. Modern compiled languages like BASIC, C and PASCAL share common 
  193. in memory organization. This is called RUN TIME ARCHITECTURE. 
  194. When your program is running it's memory is divided into these 
  195. areas. Some or all of these divisions exist in all languages but 
  196. the order they appear in memory may differ. The manual that came 
  197. with your language most likely has the details (in highly 
  198. technical terms) but below is a general synopsis of the area so 
  199. that you can understand some of the issues which follow.
  200.  
  201. Figure 1
  202.  
  203.                     to be added later
  204.  
  205.  
  206. At the lowest point in memory is the PROGRAM SEGMENT PREFIX 
  207. (PSP). This is an area that DOS constructs when your program 
  208. loads that contains various information about your program. The 
  209. PSP sometimes exists at offset 0 from the CS register but this 
  210. cannot be counted upon. It is always 100 hex bytes long. Next 
  211. above the PSP at offset 100h into the program space comes the 
  212. program CODE. The actual instructions to the microprocessor. 
  213. Above the CODE comes various segments of data storage and the 
  214. programs DS register is usually set to the base of this area, the 
  215. first of which is usually INITIALIZED DATA, sometimes called 
  216. CONSTANT data or CONST internally. This is information such as 
  217. string literals that will not change as the program executes. 
  218. Next storage space for variables whose values may change but 
  219. whose memory allocation size remains constant. Internally this is 
  220. usually called DATA. Next usually comes STACK. Here is the space 
  221. where the stack is kept and most languages default to a stack 
  222. size of 256 bytes although this does differ radically depending 
  223. on the type of language. AI languages are an exception and have a 
  224. HUGE stack because of the way they deal with recursion. Above the 
  225. stack comes an area called the HEAP (Sometimes STACK is above 
  226. HEAP). The heap is a resevoir of memory that the program can call 
  227. on if it needs more memory during execution. This is called 
  228. DYNAMIC MEMORY ALLOCATION. Remember rarely does a language fit 
  229. this description exactly so check your manuals for the specifics.
  230.  
  231. TSRSET - THE GATEWAY TO TSR PROGRAMMING
  232.  
  233. ABRACADABRA's features can all be accessed by it's one main 
  234. function, TSRSET. In fact TSRSET is the only function most 
  235. programs will ever need to turn them into TSR's. TSRSET is coded 
  236. into your program by you and the parameters you give it determine 
  237. the operating characteristics of your TSR. Let's take each 
  238. parameter, what it does and why and how. That is, HOTKEYS, TIME 
  239. SWAPPING, TERMINATE AND STAY RESIDENT, SCREEN HANDLING and 
  240. INTERRUPT 28H COMPATIBILITY. 
  241.  
  242. Let's start with parameter 4 just to be difficult. Parameter 4 
  243. defines how your program deals with memory when it terminates and 
  244. stays resident. You basically have 3 choices. Either let 
  245. ABRACADABRA figure out how much memory you need, tell it 
  246. absolutely how many paragraphs of memory to reserve for your 
  247. program, or have it set the top of memory at a convenient 
  248. location for most languages, the stack floor. That address from 
  249. which the program's stack grows downward.
  250.  
  251. If you put a zero you are telling ABRA to figure it out itself. A 
  252. well behaved, modern language does some house-cleaning 
  253. immediately upon loading. One of the things it does it turn over 
  254. any memory it isn't going to be using back to DOS. So what we 
  255. have TSRSET do is request more memory. DOS tells us the address 
  256. of the next block of unused memory and we assume that our program 
  257. lies safely below this so we truncate at this location.
  258.  
  259. If you decide to put a -1 TSRSET will use the stack, which 
  260. usually but not always delineates top of program, to determine 
  261. where to truncate. If you are using what is called a SMALL MEMORY 
  262. MODEL, TSRSET knows that the stack and stack pointer always point 
  263. to the top of the programs initial memory allocation so it 
  264. determines where the stack is, adds a couple paragraphs for 
  265. insurance and Terminates leaving your program resident. 
  266.  
  267. If you give TSRSET any value besides 0 or -1 it uses this an an 
  268. absolute value of the number of paragraphs you want to remain in 
  269. memory OF THE PROGRAM DATA SEGMENT. Most languages have their 
  270. main data segment defined largely by the location of the DS 
  271. register, and almost always ABOVE the code area, it counts the 
  272. number of paragraphs you passed it and truncates your program 
  273. there plus a few insurance paragraphs. It then moves the stack 
  274. down into this area so its current and future values are 
  275. preserved. 
  276.  
  277. To do all these terminations TSRSET uses DOS function 31h which 
  278. is the DOS Terminate and Stay Resident function.
  279.  
  280. Figure 2
  281.  
  282.                        to be added later
  283.  
  284.  
  285. Now, most languages, whether or not they make if accessible to 
  286. you the programmer or not, keep a large chunk of memory reserved 
  287. for what is called DYNAMIC MEMORY ALLOCATION. This workspace is 
  288. commonly referred to as THE HEAP. It is workspace memory that the 
  289. main program can use if it needs. For example in a language like 
  290. BASIC if you were to concatenate two strings the resulting string 
  291. has to go somewhere. The memory for it has to be requisitioned on 
  292. the fly. Languages have functions that allocate memory 
  293. DYNAMICALLY, while the program is running so everything does not 
  294. have to be preset allowing much more flexibility. Languages like 
  295. PASCAL and C have functions that the programmer can call in order 
  296. to request a piece of workspace. In C this duty is handled by the 
  297. ALLOC function family. 
  298.  
  299. In programming TSRs the HEAP is somewhat a nuisance. It is this 
  300. large piece of unused memory that you sometimes wish wasn't there 
  301. since in most cases you are trying to keep the program as small 
  302. as possible. On top of this sometimes, in what is called the 
  303. LARGE MEMORY MODELS, the compiler assumes that all the memory 
  304. past the program up to the end of physical memory is THE HEAP. 
  305. This means if your TSR has another program on top of it and the 
  306. TSR requests more memory it will simply go ahead and carve out a 
  307. chunk of the other program's space and use it (A problem we have 
  308. to live with until a PROTECTED MODE DOS becomes available) OR 
  309. think they have no HEAP space left because DOS says there is no 
  310. more memory left (Because another program is loaded therein). 
  311. Needless to say this does an effective job of killing the 
  312. computer right there. The LARGE memory models actually create 
  313. smaller programs because they keep the HEAP above the stack. 
  314. Since the HEAP is something that can or cannot be used according 
  315. to the programmer's discretion it is better that essential 
  316. program items like code and stack are kept as low in possible in 
  317. memory so that when you terminate you can amputate the HEAP 
  318. without losing that stack. 
  319.  
  320. Using other than option 0 is really butchering up the compiled 
  321. language. It's not pretty but it works.
  322.  
  323. There are several solutions to this problem. Sometimes your 
  324. language has a function to adjust the size of the HEAP in which 
  325. case you should definitely go ahead and use it to size down this 
  326. monster. You can also avoid doing dynamic memory allocation 
  327. altogether which is not that difficult and in some cases, your 
  328. TSR is a full blown program in it's own right, not just a 
  329. utility, so HEAP allocation is necessary and not a nuisance.
  330.  
  331. Ok, so now we have your program in memory and if it successfully 
  332. loads, that is, you didn't set your allocation too low, it is now 
  333. installed. The qualities it has now are dependent on the 2nd and 
  334. 3rd parameters you gave the TSRSET function. If either are zero 
  335. then the following does not apply. If non-zero, each number 
  336. determines the number of timer clock ticks each program will 
  337. receive in turn. The first parameter is the number of ticks the 
  338. normal DOS program running in the foreground gets. The second 
  339. number is the number of ticks the TCR gets. Remember, if your 
  340. program is using this feature it is a TCR (Terminate Continue 
  341. Running). 
  342.  
  343. In order to perform the switching between tasks the ABRACADABRA 
  344. library has grafted itself into a hardware interrupt commonly 
  345. known as timer tick, or interrupt 1ch. When DOS comes up 
  346. interrupt 1CH is constantly firing about every 18th of a second. 
  347. In a virgin state that interrupt simply points to an interrupt 
  348. return (IRET) instruction,  however if you are running other TSRs 
  349. they usually use this one also as it is very convenient. Now 
  350. every 18th of a second or so we receive control and can do as we 
  351. please. Each of the swapping programs (DOS normal and TSR) has 
  352. stored in it's TASK CONTROL BLOCK the number of ticks it is 
  353. allowed to execute. When a program gets swapped in we load up a 
  354. variable called PERIOD (see source code) with the tick value. 
  355. Then each time timer tick gets control it decrements this value 
  356. by 1 and checks to see if 0 has been reached (See stime procedure 
  357. in source code). When 0 is reached we begin steps to switch out 
  358. the current program and switch in the other because this program 
  359. has used up it's time slice. Now there is more to our INT 1CH 
  360. handler than that but we will take that up in a minute.
  361.  
  362. So let's assume our timer tick has counted down to zero and it is 
  363. time to swap. We don't directly swap, we simply set a variable 
  364. called doswap to 1 and wait until the necessary conditions for a 
  365. polite interruption are in existence. Timer tick is also the main 
  366. function responsible for checking these conditions. You'll notice 
  367. as you enter it that it checks several conditions before actually 
  368. invoking the SWPPRC (Swap Process) function (see source code).
  369.  
  370. So every 18th of a second or so timer tick is checking to see if 
  371. we are ready to invoke our TSR. Sometimes DOS can be busy for 
  372. quite a while so our program may not have the rhythm we would 
  373. hope. It may take several seconds from the moment it's timer 
  374. ticks have run out to the time it is swapped if something like a 
  375. disk service is in progress.
  376.  
  377. In addition to not interrupting DOS we must also beware of not 
  378. interrupting an external block device in the process of doing 
  379. something. Mainly a disk drive. We must let a disk drive complete 
  380. its request because if we in our TSR move the read/write head in 
  381. the middle of a read the interrupted program will finish off the 
  382. read or write with the head in the wrong place. Potentially very 
  383. disastrous. So we have the disk check interrupt which sits on top 
  384. of the bios disk service interrupt 13h and tells us when someone 
  385. is in there and prevents timer tick from actually invoking the 
  386. TSR.
  387.  
  388. We also do the same thing with the video interrupt 10h to avoid 
  389. interrupting a program with a half loaded set of video card 
  390. registers.
  391.  
  392. Now lets assume our TSR is not invoked by timer ticks but by the 
  393. keyboard. This is what the first parameter of TSRSET defines. 
  394. What the HOTKEY is. The detail section on TSRSET has all the 
  395. details on how to choose which key. Keyboard actions whenever 
  396. they occur cause an interrupt 9 and the CPU hands over temporary 
  397. control to whatever routine is there to handle interrupt 9. 
  398. Normally it is the BIOS but with todays TSRs which all use 
  399. HOTKEYS there can be 5 or 6 programs all waiting in line 
  400. salivating over the latest keystroke. Our interrupt 9 handler 
  401. just does a little bit of checking for the identity of the key. 
  402. If it is the key we defined as our HOTKEY is sets the doswap flag 
  403. to 1 and exits. It is now once again up to timer tick to actually 
  404. invoke our TSR when conditions are right.
  405.  
  406. Now you may have been wondering what the int28 function does. It 
  407. looks very similar to timer tick and indeed it is a timer tick 
  408. function also. There are actually 3 timer interrupts going in the 
  409. pc when it is running. interrupt 28 is an auxiliary interrupt 
  410. that was apparently installed so that PRINT.COM could run 
  411. multitasking. You see, DOS has a function INPUT LINE which 
  412. accepts a whole line of input from the keyboard. All the while 
  413. this is going on the DOS busy flag is set so timer tick cannot 
  414. interrupt the program if a doswap is triggered. It is unfortunate 
  415. that COMMAND.COM uses this function to accept command lines. IF 
  416. we had to rely solely on timer tick we could never invoke a TSR 
  417. from the DOS command line. The way to circumvent this is to use 
  418. INT28h. DOS can actually be divided into 3 separate subprograms 
  419. somewhat reflecting release versions. When one section is busy it 
  420. is possible to access another section without causing the non-
  421. reentrancy problems outlined at the beginning of this chapter.
  422.  
  423. When an interrupt 28 is active it means DOS is active on INPUT 
  424. LINE (function 0AH ) and so only functions 0CH and below are non 
  425. usable. You can use any of the functions above this without 
  426. crashing the system. Since functions 0CH and below deal only with 
  427. display output and keyboard input and are in general crummy 
  428. anyway, can design our TSRs not to use DOS for console I/O. In 
  429. fact, the BIOS itself is quite friendly when it comes to these 
  430. services. ABRACADABRA includes several functions that allow you 
  431. to do screen I/O and keyboard input without using DOS. You should 
  432. use these as opposed to language statements like PRINTF, PRINT, 
  433. INPUT, INKEY etc.
  434.  
  435. Of course if you must use these you can. However you won't be 
  436. able to invoke the TSR during INPUT LINE. The sixth parameter in 
  437. TSRSET determines this. If it is a 1 it means your TSR does not 
  438. use any DOS functions below 0CH. If it is a zero it means it does 
  439. use them and not to allow a pop up in the middle of one of these 
  440. functions.
  441.  
  442. There are some other non-minor but easily handled details to 
  443. cover.
  444.  
  445. Whenever DOS is doing disk I/O there is a 128 byte area of memory 
  446. that all the data passes through on its way to your program. It 
  447. is called the DISK TRANSFER AREA, or DTA for short. If we 
  448. interrupt a program and are going to be doing any disk I/O we 
  449. must switch that area to our own program's DTA since DOS normally 
  450. only keeps one DTA for the entire system. Otherwise we will 
  451. scramble the interrupted programs last data transfer. When we 
  452. switch our TSR out we must restore the interrupted programs 
  453. former DTA.
  454.  
  455. Whenever a critical error occurs like trying to access an open 
  456. floppy drive, DOS invokes an interrupt 24h. This is the notorious 
  457. Abort, Retry or Ignore prompt that comes up. Now we can't let a 
  458. user choose the Abort option or else we will exit the TSR and 
  459. crash the system so ABRACADABRA sets up it's own interrupt 24h 
  460. handler and tells DOS to ignore the error.
  461.  
  462. When console I/O is going on the user is allowed to press 
  463. CONTROL-C or CONTROL-BREAK to abort the program. We don't want 
  464. this to happen either. Whenever CONTROL-C is pressed DOS invokes 
  465. interrupt 23h. ABRACADABRA sets up interrupt 23h to ignore 
  466. CONTROL-C and CONTROL-BREAK. Thus the only way for the user to 
  467. exit the TSR is with your, the programmer's, permission and that 
  468. would only be by an actual TSR removal, a complex procedure to 
  469. politely release all the strings the TSR pulled to be where it 
  470. got to be.
  471.  
  472. SUMMARY
  473.  
  474. TSRSET causes your program to ride important interrupt jumps and 
  475. monitor them in order to do TSR.
  476.  
  477. Figure 3
  478.  
  479.               To be added later
  480.  
  481.  
  482. SECTION 2 MGABRA.OBJ LIBRARY FUNCTIONS 
  483.  
  484. There are two files which contain the library functions. 
  485. MGABRA.OBJ contains the actual TSR functions and is all that you 
  486. really need MGUN.OBJ contains functions that deal will 
  487. uninstalling TSRs and polling them for information about their 
  488. status.
  489.  
  490. All the functions below are far functions meaning they are called 
  491. from your program with a far call. Simply including the external 
  492. declaration file (MGABRA.H) in your source code will take care of 
  493. telling your compiler this. The primary and only absolutely 
  494. essential function to use is TSRSET. The others are there to help 
  495. you polish your TSR. Because they are in assembly language their 
  496. parameter passing is set thus the cdecl in case you are compiling 
  497. your other functions with PASCAL conventions.
  498.  
  499. TSRSET(hotkey, tsrticks, dosticks, memory, screen, int28)
  500.  
  501. PROTOTYPE:
  502. int far cdecl tsrset(int key, int dosticks, int tsrticks, int 
  503. mem, int scswap, int int28);
  504.  
  505. All the above parameters are 16 bit integer values.
  506.  
  507. The HOTKEY is passed as a word value, high byte is shift key bit 
  508. number and the low byte is the normal scan key status. So the 
  509. value 0x0201 hex would mean the HOTKEY was left shift escape 
  510. because 02 is the bit the left shift key activates when pressed 
  511. and 01 is the scan code of the escape key. APPENDIX 1 is a table 
  512. of the values associated with each key. As another NOTE your TSRs 
  513. in native state use the same HOTKEY for pop up as well as pop 
  514. out. Although once your TSR is active you can use any key combo 
  515. combined with the SWAP function (covered later) to swap out the 
  516. TSR.
  517.  
  518. Remember, these are NOT THE ASCII codes. They are a hardware 
  519. value sent by the keyboard whenever it is pressed. So you see the 
  520. Z key has only one value. This is because upper or lower case is 
  521. determined by the state of the shift keys, not solely by which 
  522. key is pressed.
  523.  
  524. The next parameter, memory is the size of memory to reserve for 
  525. the program when it terminates. This is the most complex and 
  526. maddening part of TSR programming and is covered more thoroughly 
  527. above. Bascially If you put a zero ABRACADABRA will determine 
  528. memory by asking DOS where the first area of free memory is and 
  529. truncating your program beneath that. If you put a -1 ABRACADABRA 
  530. will truncate your program at the base of it's stack and if you 
  531. put any other value ABRACADABRA will leave that many paragraphs 
  532. above the program's DS register (DATA SPACE) in memory and 
  533. relocate the stack safely within that area.
  534.  
  535. The tsrticks and dosticks parameters determine how many timer 
  536. ticks each process will get. The first parameter is how many 
  537. ticks the DOS program will get and the second is how many ticks 
  538. the TSR gets. This can be used to have a program continue to run 
  539. when it is swapped out. Remember that the timer tick occurs 18.2 
  540. times a second so if you want this value to be in seconds you 
  541. must multiply by 18.2.
  542.  
  543. The next parameter controls the manner in which the TSR will 
  544. manage screen when it is swapped. Each bit in this word has a 
  545. special function. If one, the corresponding function is enabled. 
  546. See APPENDIX 2 for a table of the specific values you can use 
  547. with this function.
  548.  
  549. The reason for the "swap by key" parameters is so you can have 
  550. your TCR running in background and if the user wants it full 
  551. blown into foreground only then will the screens swap. You 
  552. wouldn't want the screen swapping every 1/2 second if you were 
  553. running the TCR on the timer ticks. Your TCR can poll the 
  554. function SWPING and if it's true (1) then don't have it write to 
  555. the screen. If it's false (0) then that means your TCR has the 
  556. full screen so it's ok to write to it. Even better is to have 
  557. your TCR (Terminate Continue Running) write all it's output to a 
  558. video page other than 1. Then when you swap it in all the video 
  559. output it has created is live on that page. The function PUTCHR 
  560. has been provided for this purpose.
  561.  
  562. The INT28 is an option. If your TSR program uses DOS services 
  563. under 0CH you will not be able to invoke it under certain 
  564. circumstances. That is, whenever DOS is using those services. 
  565. They are all services that deal with screen I/O and keyboard 
  566. input. For greater versatility you should write your own screen 
  567. and keyboard I/O and never use language statements like PRINT and 
  568. INPUT (basic) but if you must, set this parameter to 0. You will 
  569. not be able to invoke the TSR from the COMMAND.COM prompt. If 
  570. your program does not use DOS services less than 0CH you can set 
  571. this flag to 1 which will allow you to invoke it in a greater 
  572. number of circumstances. The functions PUTCHR, CONOUT and INKEY 
  573. have been provided for you to go around DOS to do console I/O. 
  574. You can use them as primitives to construct your own console I/O 
  575. that does not use DOS.
  576.  
  577. Examples:
  578.  
  579. tsrset(0x083B, 5, 1, 0, 56, 1);
  580.  
  581. Set the HOTKEY to ALT-F1 (0x083B), have the program multitask 
  582. with 5 timer ticks of the DOS program for every 1 tick of the TCR 
  583. program. Have it figure out itself how much memory to retain (0). 
  584. Have it swap the entire screen content only when the hot key is 
  585. used (56) and it does not use any DOS functions below 0CH (1).
  586.  
  587. tsrset(0x78, 0, 0, 1000, 0, 0);
  588.  
  589. Have the hotkey set to the gray plus (+) key, no timer tick 
  590. multitasking enables, have it save 1000 paragraphs of memory when 
  591. it initially loads and terminates. No inherent screen control 
  592. active and it uses DOS functions less than 0CH so don't let it 
  593. come up when DOS is inputting a line of text.
  594.  
  595. SWPSCR(mode)
  596.  
  597. PROTOTYPE:
  598. int far cdecl swpscr(int mode);
  599.  
  600. SWPSCR allows you to change the swapscreen parameter that you 
  601. originally set with TSRSET. Your TSR can dynamically change the 
  602. way it treats the screen by resetting the screen swap mode with 
  603. this function. The values of mode are the same as those used by 
  604. the TSRSET function. Sometimes your TSR will pop up a window and 
  605. you don't want to switch the whole screen, just the cursor, so 
  606. you can put a 1 here. If you want to whole screen swapped those 
  607. options are also available. In it's default state ABRACADABRA 
  608. ensures that no ugly snow appears on the screen when using the 
  609. Color Graphics Adapter. This slows down screen swapping so if 
  610. snow isn't a problem you can set the 256 bit on and it will skip 
  611. checking for snow.
  612.  
  613. HOTKEY(key)
  614.  
  615. PROTOTYPE:
  616. int far cdecl hotkey(int keyval);
  617.  
  618. The HOTKEY function lets your TSR dynamically change the key 
  619. which activates it. The value of key is the same as that used in 
  620. TSRSET to initially set the hotkey. You can disable the hotkey by 
  621. assigning key the value 0.
  622.  
  623. Example:
  624.  
  625. hotkey(0);
  626.  
  627. disables HOTKEY action altogether.
  628.  
  629. hotkey(0x0839);
  630.  
  631. Sets the HOTKEY to ALT SPACE because 08 is the bit pattern for 
  632. Alt key press and 39h is the scan code of the space bar. Remember 
  633. this parameter takes a 16 bit integer so passing it in hex makes 
  634. it easier to see because the high byte is the first two hex 
  635. digits and the low byte is the second two.
  636.  
  637. TIMER(tsrticks, dosticks)
  638.  
  639. PROTOTYPE:
  640. int far cdecl timer(int dosticks, int tsrticks);
  641.  
  642. The TIMER function allows your TSR to change the value of the 
  643. ticks parameters initially set with TSRSET. You can disable the 
  644. timer altogether by the statement timer(0, 0);.
  645.  
  646. Example:
  647.  
  648. timer(10, 1);
  649.  
  650. This gives the TSR 1 timer tick for every 10 of the foreground 
  651. DOS program.
  652.  
  653. SWAP
  654.  
  655. PROTOTYPE:
  656. int far cdecl swap(void);
  657.  
  658. The swap function takes no parameters. It is used by your TSR 
  659. to exit and swap back in the program that was interrupted. So a 
  660. TSR can tell itself to swap out and this doesn't have to be 
  661. determined by a hot key only.
  662.  
  663. Example: 
  664. if (time == 012099) swap();
  665.  
  666. SWPING
  667.  
  668. PROTOTYPE:
  669. int far cdecl swping(void);
  670.  
  671. SWPING is a function that sets nothing but returns a non-zero 
  672. value if the timer tick activation is currently on (meaning 
  673. multi-tasking is occurring) and 0 if the timer is disabled. If 
  674. you want your TSR to base some of it's activity on whether it is 
  675. multitasking or not you can use this function to determine that.
  676.  
  677. Example: 
  678.  
  679. if (!swping()) conout("Hello There!");
  680.  
  681. Will ensure that your TCR won't print to the screen while 
  682. another program is running. It will only print this if the TCR is 
  683. switched in fully.
  684.  
  685. SWAPNO, SWAPYES
  686.  
  687. PROTOTYPES:
  688. int far cdecl swapno(void);
  689. int far cdecl swapyes(void);
  690.  
  691. SWAPNO enables or disables swapping entirely. Sometimes there 
  692. may be a critical function happening and you don't want swapping 
  693. to occur so you call SWAPNO. To re-enable swapping call SWAPYES.
  694.  
  695. Example:
  696.  
  697. swapno();
  698.  
  699. Would disable swapping entirely. The only way to re-enable it 
  700. is for the TSR to execute
  701.  
  702. swapyes();
  703.  
  704. STATUS(value)
  705.  
  706. PROTOTYPE:
  707. int far cdecl status(int val);
  708.  
  709. STATUS is a way for you to set a value which tells other MAGIC 
  710. tsrs when they ask, what the status is of this TSR. For example 
  711. if you want to remove a TSR you ask it first if it is OK (It may 
  712. have files open or other non-interruptable procedures). You 
  713. determine what the values mean, status just sets a value from 0 
  714. to 65535.
  715.  
  716. Example: 
  717.  
  718. status(12)
  719.  
  720. Now when any program asks PROCST(<tsr address>); it would 
  721. receive a 12 in return. You can have the numbers mean whatever 
  722. you want and supposedly your collection of TSRs would share a 
  723. common set of status codes.
  724.  
  725. ID(number)
  726.  
  727. PROTOTYPE:
  728. int far cdecl id(int id);
  729.  
  730. ID is like status except it allows you to have the TSR set a 
  731. number for an identification. If you write multiple TSRs and have 
  732. a collection each should call ID with it's own unique number. 
  733. That way programs which poll the TSRs know which one they are 
  734. talking to.
  735.  
  736. INKEY
  737.  
  738. PROTOTYPE:
  739. int far cdecl inkey(void);
  740.  
  741. Inkey waits for the next keystroke and returns an int value 
  742. representing it. It is talking directly to the BIOS so bypasses 
  743. DOS. This way you don't have to worry about the interrupt 28h 
  744. problem. The returned integer value has to be broken down. The 
  745. high byte is the ASCII code of the character typed. If it is a 
  746. zero it means a special key was typed, like F1 or PGUP. The low 
  747. byte is the scan code of the key that was typed. You can do 
  748. whatever you want with this information. Remember that INKEY does 
  749. not echo to the screen. YOU have to write that in.
  750.  
  751. Example: 
  752.  
  753. int k;
  754. char h;
  755. char *s;
  756. conout("Enter Your Name");
  757. k = inkey();
  758. h = k/256;
  759. while(h <> "\015"){
  760.    *s++ = h;   
  761.    k = inkey();
  762.    h = k/256;
  763. }
  764.  
  765. CONOUT(string)
  766.  
  767. PROTOTYPE:
  768. int far cdecl conout(char far *s);
  769.  
  770. You pass conout the address of the null terminated string and 
  771. it outputs the string on the console. It bypasses DOS and thus 
  772. gets around the interrupt 28 problem. It is possible to use 
  773. printf like functions. You have to use SPRINTF which sends the 
  774. formatted string to memory and then use conout to send that 
  775. memory block to the console. Cursor position is automatically 
  776. updated to the end of the string. CONOUT uses BIOS video function 
  777. 14.
  778.  
  779. Example:
  780. conout("Hey dude! Your TSR is working! Let's Party!");
  781.  
  782. PUTCHR(cursor row, column, video page, attribute, character) 
  783.  
  784. PROTOTYPE:
  785. int far cdecl putchr(int row, int col, int page, int att, char 
  786. c);
  787.  
  788. Putchr allows a pin point placement of a character anywhere in 
  789. the pc's video pages with whatever attribute you want. You pass 
  790. it all this information. With this service however the cursor 
  791. position is not automatically updated. You have to handle that 
  792. yourself.
  793.  
  794. Example:
  795.  
  796. putchr(12, 40, 2, 16, 'X');
  797.  
  798. Would put an X in the middle of video page 2 with color 
  799. attribute 16.
  800.  
  801. MGUN FUNCTIONS
  802.  
  803. The functions in MGUN deal with de-installing TSRS.
  804.  
  805. TSRS
  806.  
  807. PROTOTYPE:
  808. int far cdecl tsrs(void);
  809.  
  810. TSRS returns non-zero if any MAGIC TSR's are in memory and 0 
  811. if not.
  812.  
  813. FIRSTSR
  814.  
  815. PROTOTYPE:
  816. int far * cdecl far firstsr(void);
  817.  
  818. FIRSTSR returns a 32 bit pointer to the first TSR in the chain 
  819. of TSR's in memory. Note: it does not point to the TSRs PSP or 
  820. base, only to it's external access function CLEAR. This is so 
  821. other routines, now knowing this address, can call it and get 
  822. information about the TSR. If FIRSTSR returns a 0 there are no 
  823. MAGIC TSRs in memory.
  824.  
  825. You don't have to treat this pointer as other than a 4 byte 
  826. value as you will only use it as a parameter to the other 
  827. functions in this section. i.e. don't get muddled about what how 
  828. you are going to manipulate a 4 byte pointer, just declare the 
  829. pointer variable as such and use it. The internals are taken care 
  830. of.
  831.  
  832. PROCID(tsr address)
  833.  
  834. PROTOTYPE:
  835. int far cdecl procid(int far *f);
  836.  
  837. PROCID when passed the address of a TSR (Determined with 
  838. FIRSTSR or NEXTSR) will return the integer value set with ID by 
  839. the TSR. You can tell which TSR is which by using this function.
  840.  
  841. PROCST(tsr address)
  842.  
  843. PROTOTYPE:
  844. int far cdecl procst(int far *f);
  845.  
  846. PROCST when given the address of a TSR will return the status 
  847. code set with the STATUS function. You can tell what a TSR is up 
  848. to if it sets this appropriately with STATUS. Remember, the status 
  849. is just a number. You determine what it means and your collection 
  850. of TSRs should all share a common set of status meanings.
  851.  
  852. NEXTSR(tsr address)
  853.  
  854. PROTOTYPE:
  855. int far * cdecl far nextsr();
  856.  
  857. Given the address of one TSR NEXTSR will return the address of 
  858. the next one. If it returns a 0 there is no next one.
  859.  
  860. UN(tsr address)
  861.  
  862. PROTOTYPE:
  863. int far cdecl un(int far *f);
  864.  
  865. UN when passed a TSR address will uninstall it and return to 
  866. DOS any memory it was using. A TSR cannot UN itself when it is in 
  867. residency. TSR's MUST be uninstalled in reverse order they were 
  868. installed. This may seem inconvenient but in reality MS-DOS does 
  869. not deallocate memory until memory above is deallocated so it 
  870. doesn't do any good to de-install a TSR below another one.
  871.  
  872. CHECK(id)
  873.  
  874. PROTOTYPE:
  875. int far * cdecl far check(int id);
  876.  
  877. If you give check an id it will return a far (32 bit) pointer 
  878. the tsr with that id is in memory or a zero if it isn't. You can 
  879. use it in the beginning of a program load and give a message like 
  880. "Program Already Loaded" so they don't load a duplicate of the 
  881. thing.
  882.  
  883. SUMMING IT ALL UP
  884.  
  885. Now that we have looked at all the theory behind what goes on 
  886. lets run down step by step what you have to do to create your 
  887. TSR.
  888.  
  889. 1. Write your program as a normal DOS program BUT use the special 
  890. MGABRA console I/O functions, CONOUT, PUTCHR and INKEY. Debug it 
  891. thoroughly. Remember a TSR crashing will crash the whole system. 
  892. Also put in STATUS functions with appropriate values wherever the 
  893. program is doing something that makes it dangerous to remove from 
  894. memory.
  895.  
  896. 2. Decide where your program is going to put it's video output. 
  897. If it will run concurrently with the foreground DOS program you 
  898. can't have it printing to video page 0 ALL the time.
  899.  
  900. 3. Remove any code that lets the program terminate. A TSR can 
  901. NEVER terminate like a normal program. It must be uninstalled. 
  902. You can leave CONTROL-BREAK active for now though because the 
  903. next step will seal that one up. 
  904.  
  905. 4. Add the TSRSET function near the beginning of the program, 
  906. preferably immediately after you have set up the screen mode for 
  907. your program. Also call the ID function with a number for program 
  908. identification just before you call TSRSET.
  909.  
  910. 5. Use 0 for parameter 4 of TSRSET. Compile, link and execute. If 
  911. the system crashes you should experiment with non-zero values for 
  912. this until the program loads without crashing. Sometimes it is 
  913. better to make all the TSRSET parameters command line changeable 
  914. so you can rapidly get the best combination before you hard code 
  915. them in.
  916.  
  917. 6. Run your TSR and fine tune it with the other MGABRA and MGUN 
  918. functions.
  919.  
  920. PROBLEMS
  921.  
  922. Here are some common problems and their solutions:
  923.  
  924. WHEN TSR/TCR IS INVOKED THE SYSTEM CRASHES
  925.  
  926. Ok. Before you make your program a TSR, run it as a normal 
  927. program. If it runs ok only then have it call TSRSET. If it then 
  928. crashes the problem is most likely that the stack has been moved 
  929. into the code and scrambled the interrupt handlers. Just give 
  930. your TSR more resident space. Always start with parameter 4 at 
  931. zero as this is the safest route.
  932.  
  933. TSR LOADS FINE BUT SYSTEM CRASHES AS SOON AS NEXT PROGRAM IS 
  934. LOADED
  935.  
  936. The problem is the same as the above except in this case the 
  937. next program load is loading into the TSR's interrupt vector code 
  938. and scrambling it. The TSR has simply been truncated too short. 
  939. Increase the value of the fourth parameter in TSRSET.
  940.  
  941. TSR LOADS UP BUT DATA IS SCRAMBLED WHEN I RUN IT OR IT GOES 
  942. OFF INTO NEVER NEVER LAND AND THE SCREEN IS FULL OF GARBAGE.
  943.  
  944. Some run time architecture puts the stack beneath the data at 
  945. runtime. You have to manually experiment with the proper value 
  946. for the 4th parameter in TSRSET. Set it quite large, say 10,000 and 
  947. work down getting the lowest possible value you can without 
  948. crashing it. That 4th parameter basically relocates the stack. 
  949. You want to make sure it goes above all your CONSTANT DATA and 
  950. HEAP.
  951.  
  952. TSR LOADS AND EXECUTES BUT SYSTEM CRASHES AFTER I RETURN TO 
  953. INTERRUPTED PROGRAM.
  954.  
  955. There is a good possibility that the TSR courrupted the other 
  956. program's memory. Remember if you are using a large memory model 
  957. runtime architecture with your compiled language that using 
  958. dynamic memory allocation will cause the TSR to allocate memory 
  959. from possibly already in use areas. Very nasty as it could be 
  960. eating the interrupted program. Try a small memory model and see 
  961. if it's ok then.
  962.  
  963. TSR LOADS UP BUT SYSTEM GIVES FATAL: INTERNAL STACK ERROR 
  964. AFTER I INVOKE THE TSR AND RETURN TO THE INTERRUPTED PROGRAM.
  965.  
  966. Some of your code is using DOS functions 0CH or less to do 
  967. console I/O. You are probably using WRITELN, WRITE, PRINTF, 
  968. PRINT, INKEY, INPUT, SCANF or something like that. Instead use 
  969. our provided functions for this or else put a 0 in the sixth 
  970. parameter of TSRSET.
  971.  
  972. TSR LOADS UP BUT SYSTEM CRASHES WHEN I TRY TO INVOKE IT
  973.  
  974. Almost always a stack problem as outlined above. Experiment 
  975. with different values for the 4th TSRSET parameter.
  976.  
  977. IMPORTANT TIPS ON WRITING TSRS
  978.  
  979. Remember your TSR is running somewhat in violation of the laws 
  980. of DOS so there are some things you can't do.
  981.  
  982. Don't have your program terminate normally. If you do DOS returns 
  983. to COMMAND.COM and you'll have two COMMAND.COMs operating side by 
  984. side and not for long either. Any second the system will go 
  985. splat. Of course this could be interesting but unless that is the 
  986. intent of your program the user will get very confused. The only 
  987. way to remove your TSR gracefully from memory is with the UN 
  988. function. If you terminate that way your interrupt handlers are 
  989. still live and DOS will de-allocate them at which point your 
  990. machine will go bye bye.
  991.  
  992. If you have a TCR like a modem program that runs in background 
  993. remember not to have it write to the console because it will 
  994. overwrite the foreground program. While not fatal it is messy. 
  995. You can have your TCR (with CGA or EGA) write to a separate video 
  996. page. That way when you invoke it with the HOTKEY all the data 
  997. that has been scrolling by will be there. Or you could use the 
  998. SWPING function and only allow it to write when it isn't 
  999. multitasking. Of course this way you lose any screen output 
  1000. occurring during that time. There are ways around this and 
  1001. ABRACADABRA gives you the primitives to implement it. You have to 
  1002. use your imagination. But then, that's why you are a programmer 
  1003. isn't it?
  1004.  
  1005. In general since TSR's are meant to be utilities you want them to 
  1006. be as small as possible. If you are using a compiled language you 
  1007. will run into the problem that most of them allocate minimally 
  1008. 64k to data ON TOP OF CODE. Some of the languages provide 
  1009. functions to size down that area so use those before you call 
  1010. TSRSET. Using the default of 0 for parameter 4 is not always the 
  1011. best thing to do. You may have to manually experiment with the 
  1012. best value here. For C we have found the COMPACT model to create 
  1013. the smallest TSRs, not the SMALL or TINY models.
  1014.  
  1015. In all our endeavors here at MAGIC we have found that the context 
  1016. switching is the most robust section of ABRACADABRA. Swapping 
  1017. processes is NEVER the problem. 95% of the problems are caused by 
  1018. the initial Terminate and Stay Resident phase and how much data 
  1019. and stack space remains. Once you get by that hassle you are home 
  1020. free.  The other 5% are non-fatal and are caused by screen, 
  1021. cursor and keyboard conflicts. For example, some programs do not 
  1022. use the BIOS to position the cursor so when we get their cursor 
  1023. position to save it prior to swapping it is inaccurate and thus 
  1024. restores to a funny position when they swap back in. Non-Fatal of 
  1025. course but messy. It usually re-positions on the first keystroke. 
  1026. Some machines like the Compaqs give an electronic POP and blank 
  1027. out noticeably when you swap screens. You can also hear little 
  1028. electronic chatter from the box when they are writing to other 
  1029. than video page 0. There's nothing to be done about that for now, 
  1030. it's a hardware problem!
  1031.  
  1032. You can't have two or more multitasking TCRs running at once. 
  1033. When they swap they start choking each other by restoring a half 
  1034. swapped foreground process. You end up with just one in control 
  1035. of the entire machine unable to swap out. 
  1036.  
  1037. Remember to have your TSR's set status codes appropriate to their 
  1038. conditions. If you open files make sure the user doesn't un-
  1039. install it without closing them first or else he'll lose that 
  1040. data. You use the STATUS function for that. 
  1041.  
  1042. Call TSRSET early on because once it is used you cannot return to 
  1043. any code prior to it's calling. Because it has possibly truncated 
  1044. any stack returns prior to it's calling.
  1045.  
  1046. Run your programs as NON-TSR's first to make sure they are fully 
  1047. debugged. A TSR crashing usually crashes the whole system, not 
  1048. just itself.
  1049.  
  1050. You may have custom needs for which the default ABRACADABRA 
  1051. library is not suited. For that you will need to also purchase 
  1052. the source code. It is easy to modify and well documented.
  1053.  
  1054. If you are working in the TURBO C integrated environment don't 
  1055. RUN your TSR from within it. Otherwise you are loading as a child 
  1056. process of TURBO C and when you exit TURBO C your TSR will be 
  1057. removed from memory which means all the interrupts you were 
  1058. orchestrating will be going nowhere and the system will 
  1059. immediately crash.
  1060.  
  1061. If you have the source code remember when you assemble it to use 
  1062. the /mx option to preserve case sensitivity. Otherwise when you 
  1063. link, all the ABRACADABRA externals will not resolve.
  1064.  
  1065. All the ABRACADABRA functions are FAR FUNCTIONS so you can use 
  1066. them with any of the memory models. We have found that the 
  1067. smallest TSRs result when using the compact model but on occasion 
  1068. they don't work at all with that memory model.
  1069.  
  1070. We here at MAGIC like to consider ourselves hackers in the 
  1071. lowliest sense of the word and we are here to support what we 
  1072. hope is an upcoming generation of youngsters who consider the 
  1073. ability to program as a prerequisite to life.
  1074.  
  1075. Happy Hacking!
  1076.  
  1077. APPENDIX 1 Key Scan Code Values SHIFT KEY VALUES (high byte)
  1078.  
  1079.     0 = normal
  1080.     1 = right shift
  1081.     2 = left shift
  1082.     4 = control
  1083.     8 = alt
  1084.    16 = scroll lock
  1085.    32 = num lock
  1086.   128 = insert
  1087.  
  1088. NORMAL KEY SCAN CODES (low byte)
  1089.  
  1090. KEY  SCAN CODE    KEY  SCAN CODE    KEY  SCAN CODE  KEY  SCAN CODE 
  1091. ===  =========    ===  =========    ===  =========  ===  ========= 
  1092. 1         2       K         37      ;         39    BACKARROW 14 
  1093. 2         3       L         38      '         40    RETURN    28
  1094. 3         4       M         50      `         41    GREY -    74
  1095. 4         5       N         49      \         43    GREY +    78
  1096. 5         6       O         24      ,         51    HOME      71
  1097. 6         7       P         25      /         53    PGUP      73
  1098. 7         8       Q         16      *         55    PGDN      81
  1099. 8         9       R         19      SPACE     57    END       79
  1100. 9         10      S         31      ESCAPE    1     UP        72
  1101. 0         11      T         20      F1        59    DOWN      80
  1102. A         30      U         22      F2        60    RIGHT     77
  1103. B         48      V         47      F3        61    LEFT      75
  1104. C         46      W         17      F4        62    
  1105. D         32      X         45      F5        63    
  1106. E         18      Y         21      F6        64    
  1107. F         33      Z         44      F7        65
  1108. G         34      -         12      F8        66
  1109. H         35      =         13      F9        67
  1110. I         23      [         26      F10       68
  1111. J         36      ]         27      .         52
  1112.  
  1113.  
  1114. APPENDIX 2 Screen Swap Parameter Values
  1115.  
  1116.  
  1117.     1 = swap cursor whenever programs swap
  1118.     2 = save screen whenever programs swap
  1119.     4 = restore screen whenever programs swap
  1120.     8 = swap cursor only if TSR swap by key
  1121.    16 = save screen only if TSR swap by key
  1122.    32 = restore screen only if TSR swap by key
  1123.   256 = don't take any precautions to avoid "snow" on screen
  1124.  
  1125.                                                                                              
  1126.